﻿#region  
//=====================================================
// 文件名: BundleEditor                
// 创建者: #AuthorName#                
// 创建时间: #CreateTime#              	
// Email: #AuthorEmail#               
// 描述: 当前脚本的功能              
// 修改者列表: 修改者名字以及修改功能      
// 版权所有: #Copyright#                 
//======================================================
#endregion 



using FrameWork.AB;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Xml.Serialization;
using UnityEditor;
using UnityEngine;


namespace FrameWork.Editor
{
    public class BundleEditor
    {
        private static string m_ABPackagePath = Application.streamingAssetsPath;

        private static string ABCONFIGATH = "Assets/FrameWork/_Scripts/RunTime/AssetBundle/ABConfig.asset";

        private static string m_ABConfigurationFile_XMLPATH = "Assets/FrameWork/Config/XML/AssetBundleConfig.xml";

        private static string m_ABConfigurationFile_BYTEPATH = "Assets/FrameWork/Config/Binary/AssetBundleConfig.bytes";

        private static Dictionary<string, List<string>> m_AllPrefabPath = new Dictionary<string, List<string>>();

        private static Dictionary<string, string> m_AllFileABPath = new Dictionary<string, string>();

        private static List<string> m_AllFileAB = new List<string>();

        private static List<string> m_ValidPath = new List<string>();

        [MenuItem("QFrameWork/AB Build")]
        public static void Build()
        {
            //  清除数据
            m_ValidPath.Clear();
            m_AllFileAB.Clear();
            m_AllPrefabPath.Clear();
            m_AllFileABPath.Clear();

            //获取配置文件
            ABConfig abconfig = AssetDatabase.LoadAssetAtPath<ABConfig>(ABCONFIGATH);

            //添加文件夹
            int progress = 0;
            foreach (var item in abconfig.m_AllFileDirAB)
            {
                progress++;
                if (m_AllFileABPath.ContainsKey(item.ABName))
                {
                    Debug.LogError("The AB package configuration file name is repeated , name：" + item.ABName);
                }
                else
                {
                    m_AllFileABPath.Add(item.ABName, item.Path);
                    m_AllFileAB.Add(item.Path);
                    m_ValidPath.Add(item.Path);
                    EditorUtility.DisplayProgressBar("AB profile", "filename：" + item.ABName, progress * 0.1f / abconfig.m_AllFileDirAB.Count);
                }
            }
            //添加预制体，包括依赖
            string[] allPrefabStr = AssetDatabase.FindAssets("t:Prefab", abconfig.m_AllPrefabPath.ToArray());
            for (int i = 0; i < allPrefabStr.Length; i++)
            {
                string path = AssetDatabase.GUIDToAssetPath(allPrefabStr[i]);
                m_ValidPath.Add(path);
                EditorUtility.DisplayProgressBar("AB  Prefabs", "prefab  Name：" + path, i * 0.1f / allPrefabStr.Length);
                if (!IsAlreadyIncluddedInTheExistingABPackage(path))
                {
                    GameObject obj = AssetDatabase.LoadAssetAtPath<GameObject>(path);
                    string[] contaion = AssetDatabase.GetDependencies(path);
                    List<string> containList = new List<string>();
                    for (int j = 0; j < contaion.Length; j++)
                    {
                        if (!IsAlreadyIncluddedInTheExistingABPackage(contaion[j])  && !contaion[j].EndsWith(".cs"))
                        {
                            m_AllFileAB.Add(contaion[j]);
                            containList.Add(contaion[j]);
                        }
                    }
                    if (m_AllPrefabPath.ContainsKey(obj.name))
                    {
                        Debug.LogError("The Same prefabricated path already exists:pathName" + obj.name);
                    }
                    else
                    {
                        m_AllPrefabPath.Add(obj.name, containList);
                    }

                }
            }

            SetFileAndPrefabABPackageName();

            BunildAssetBundle();

            RemoveABPackageName();

            AssetDatabase.Refresh();

            EditorUtility.ClearProgressBar();

        }

        static void SetFileAndPrefabABPackageName()
        {
            foreach (var item in m_AllFileABPath)
            {
                SetABName(item.Key, item.Value);
            }
            foreach (var contaion in m_AllPrefabPath)
            {
                SetABName(contaion.Key, contaion.Value);
            }
        }

        static void SetABName(string _name, List<string> paths)
        {
            for (int i = 0; i < paths.Count; i++)
            {
                SetABName(_name, paths[i]);
            }
        }

        static void SetABName(string _name, string path)
        {
            AssetImporter assetImporter = AssetImporter.GetAtPath(path);
            if (assetImporter == null)
            {
                Debug.LogError("There is no file for this path");
            }
            else
            {
                assetImporter.assetBundleName = _name;
            }
        }

        static void RemoveABPackageName()
        {
            string[] oldABNames = AssetDatabase.GetAllAssetBundleNames();
            for (int i = 0; i < oldABNames.Length; i++)
            {
                AssetDatabase.RemoveAssetBundleName(oldABNames[i], true);
                EditorUtility.DisplayProgressBar("Remove AB package name!", "name:" + oldABNames[i], i * 1.0f / oldABNames.Length);
            }
        }

        static void BunildAssetBundle()
        {
            string[] allBundle = AssetDatabase.GetAllAssetBundleNames();
            Dictionary<string, string> resPathDic = new Dictionary<string, string>();
            for (int i = 0; i < allBundle.Length; i++)
            {
                string[] allbundlePath = AssetDatabase.GetAssetPathsFromAssetBundle(allBundle[i]);
                for (int j = 0; j < allbundlePath.Length; j++)
                {
                    if (allbundlePath[j].EndsWith(".cs"))
                    {
                        continue;
                    }
                    //Debug.Log("ABPackage:[" + allBundle[i] + "] contain :  " + allbundlePath[j]);
                    if (ValidPath(allbundlePath[j]))
                    {
                        resPathDic.Add(allbundlePath[j], allBundle[i]);
                    }


                }
            }

            //删除没有用的AB包
            DeletedABPackage();

            //生成配置表
            WriteData(resPathDic);

            BuildPipeline.BuildAssetBundles(m_ABPackagePath, BuildAssetBundleOptions.None,
               BuildTarget.StandaloneWindows64);

        }

        static void WriteData(Dictionary<string, string> resPathDic)
        {
            AssetBundleConfig config = new AssetBundleConfig();
            config.ABList = new List<ABBase>();
            foreach (string item in resPathDic.Keys)
            {
                ABBase aBBase = new ABBase();
                aBBase.Path = item;
                aBBase.Crc = CRC32.GetCRC32(item);
                aBBase.ABPackageName = resPathDic[item];
                aBBase.AssetName = item.Remove(0, item.LastIndexOf("/") + 1);
                //Debug.Log("AssetName:" + aBBase.AssetName);
                aBBase.ABDependce = new List<string>();
                string[] resDependce = AssetDatabase.GetDependencies(item);
                for (int j = 0; j < resDependce.Length; j++)
                {
                    string tempPath = resDependce[j];
                    if (tempPath == item || item.EndsWith(".cs"))
                    {
                        continue;
                    }
                    string abName = string.Empty;
                    if (resPathDic.TryGetValue(tempPath, out abName))
                    {
                        if (abName == resPathDic[item])
                        {
                            continue;
                        }
                        if (!aBBase.ABDependce.Contains(abName))
                        {
                            aBBase.ABDependce.Add(abName);
                        }
                    }
                }
                config.ABList.Add(aBBase);
            }

            //写入XML
            string xmlPath = m_ABConfigurationFile_XMLPATH;
            if (File.Exists(xmlPath)) File.Delete(xmlPath);
            FileStream filestream = new FileStream(
                xmlPath,
                FileMode.Create,
                FileAccess.ReadWrite,
                FileShare.ReadWrite);
            StreamWriter sw = new StreamWriter(filestream, System.Text.Encoding.UTF8);
            XmlSerializer xs = new XmlSerializer(config.GetType());
            xs.Serialize(sw, config);
            sw.Close();
            filestream.Close();

            //写入二进制
            foreach (ABBase abBase in config.ABList)
            {
                abBase.Path = string.Empty;
            }
            string bytePath = m_ABConfigurationFile_BYTEPATH;
            FileStream fs = new FileStream(bytePath, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite);
            BinaryFormatter bf = new BinaryFormatter();
            bf.Serialize(fs, config);
            fs.Close();


        }

        static void DeletedABPackage()
        {
            string[] allBundleName = AssetDatabase.GetAllAssetBundleNames();
            DirectoryInfo direction = new DirectoryInfo(m_ABPackagePath);
            FileInfo[] files = direction.GetFiles("*", SearchOption.AllDirectories);
            for (int i = 0; i < files.Length; i++)
            {
                if (ConatinABName(files[i].Name, allBundleName) || files[i].Name.EndsWith(".meta"))
                {
                    continue;
                }
                else
                {
                    File.Delete(files[i].FullName);
                    File.Delete(files[i].FullName + ".meta");
                }
            }

        }

        static bool ConatinABName(string _name, string[] _strs)
        {
            for (int i = 0; i < _strs.Length; i++)
            {
                if (_name == _strs[i])
                {
                    return true;
                }
            }
            return false;
        }

        static bool ValidPath(string path)
        {
            for (int i = 0; i < m_ValidPath.Count; i++)
            {
                if (path.Contains(m_ValidPath[i]))
                {
                    return true;
                }
            }
            return false;
        }

        static bool IsAlreadyIncluddedInTheExistingABPackage(string path)
        {
            for (int i = 0; i < m_AllFileAB.Count; i++)
            {
                if (path == m_AllFileAB[i] || (path.Contains(m_AllFileAB[i]) && path.Replace(m_AllFileAB[i], "")[0] == '/'))
                {
                   
                    return true;
                }
            }
            return false;
        }
    }
}